using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;


namespace Stopwatch
{
    public partial class Form1 : Form
    {

        private Stopwatch sw;
        private RunReport runRep;
        private TimeSpan SplitTime;
        private TimeSpan LastSplitTime;
        private TimeSpan LapTime;
        private DataSet dsReport;
        private DataTable TableLaps;
        private DataTable TableSummary;
        private TimeSpan fastestLap;
        private TimeSpan slowestLap;
        private TimeSpan averageLap;
        private string sFastestLap;
        private string sSlowestLap;
        private double dAverage;
        private double dTotalLaps;
        private int iNumReportSaved;
        private int iMaxNumberOfRepSaved;
        private int iMaxLaps;

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                //StopWatch class
                sw = new Stopwatch();
                //RunReport class
                runRep = new RunReport();

                //Get settings from registry
                GetValuesFromRegistry();
                
                //Prepare Dataset and DataTables to receive Lap data
                PrepareDataObjects();

                //Update View Report menu item
                UpdateNumReportSaved();

                //Time structures
                TimeSpan fastestLap = new TimeSpan();
                TimeSpan slowestLap = new TimeSpan();
                TimeSpan averageLap = new TimeSpan();
                string sFastestLap = string.Empty;
                string sSlowestLap = string.Empty;
            }
            catch (Exception ex)
            {
                Debug.WriteLine(String.Format("Stopwatch Form1_Load Exception Msg: {0} Stack Info: {1}",
                    ex.Message, ex.StackTrace));
            }
        }

        private void UpdateNumReportSaved()
        {
            //Check if directory has any xml report saved
            iNumReportSaved = runRep.GetReportsFromFolder().Length;

            if (iNumReportSaved > 0)
                mniViewReport.Enabled = true;
        }

        private void GetValuesFromRegistry()
        {
            RegistryOper regOper = new RegistryOper();

            try
            {
                iMaxNumberOfRepSaved = regOper.GetMaxNumberOfSavedReports();
                iMaxLaps = regOper.GetMaxNumberOfLaps();
            }
            catch (Exception ex)
            {
                //if any exception occurs, get default values
                iMaxNumberOfRepSaved = Common.DEFAULT_MAX_NUMBER_SAVED_REPORTS;
                iMaxLaps = Common.DEFAULT_MAX_NUMBER_LAPS;

                Debug.WriteLine(String.Format("Stopwatch GetValuesFromRegistry Exception Msg: {0} Stack Info: {1}",
                    ex.Message, ex.StackTrace));
            }
        }

        private void PrepareDataObjects()
        {
            dsReport = new DataSet("RunReport");

            TableLaps = new DataTable("LapsTable");
            DataColumn LapInfo = new DataColumn("LapInfo", System.Type.GetType("System.String"));
            DataColumn LapIndex = new DataColumn("LapIndex", System.Type.GetType("System.Int16"));
            TableLaps.Columns.Add(LapInfo);
            TableLaps.Columns.Add(LapIndex);

            TableSummary = new DataTable("SummaryTable");
            DataColumn clFastest = new DataColumn("Fastest", System.Type.GetType("System.String"));
            DataColumn clSlowest = new DataColumn("Slowest", System.Type.GetType("System.String"));
            DataColumn clAverage = new DataColumn("Average", System.Type.GetType("System.String"));
            DataColumn clLapCount = new DataColumn("LapCount", System.Type.GetType("System.String"));
            DataColumn clReportNumber = new DataColumn("ReportNumber", System.Type.GetType("System.Int16"));
            TableSummary.Columns.Add(clFastest);
            TableSummary.Columns.Add(clSlowest);
            TableSummary.Columns.Add(clAverage);
            TableSummary.Columns.Add(clLapCount);
            TableSummary.Columns.Add(clReportNumber);
        }


        private void PrepareTableSumary(string sFastest, string sSlowest, string sAverage, int iLapCount, int iReportNumber)
        {
            
            try
            {
                TableSummary.Clear();

                DataRow TableRow = TableSummary.NewRow();
                TableRow["Fastest"] = sFastest;
                TableRow["Slowest"] = sSlowest;
                TableRow["Average"] = sAverage;
                TableRow["LapCount"] = iLapCount.ToString();
                TableRow["ReportNumber"] = iReportNumber;
                TableSummary.Rows.Add(TableRow);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            SplitTime = sw.GetSplitTimeSpan();
            lbHour.Text = String.Format("{0:00}:", SplitTime.Hours);
            lbMinute.Text = String.Format("{0:00}:", SplitTime.Minutes);
            lbSecond.Text = String.Format("{0:00}.", SplitTime.Seconds);
            lbMilisecond.Text = String.Format("{0:000}", SplitTime.Milliseconds);

            if(SplitTime.Seconds == Common.MAX_LIMIT_SECOND &&
               SplitTime.Minutes == Common.MAX_LIMIT_MINUTE && 
               SplitTime.Hours == Common.MAX_LIMIT_HOUR)
            {
                AddLap();
                StopStopwatch();
            }
        }


        private void mniStart_Click(object sender, EventArgs e)
        {
            if (mniStart.Text.Trim() == "Start")
            {
                StartStopwatch();
            }
            else
            {
                AddLap();
                StopStopwatch();
            }
        }

        private void StopStopwatch()
        {
            timer1.Enabled = false;

            sw.Stop();

            //Update Fastest, Slowlest and Average
            UpdateLabels();

            mniStart.Text = "Start";

            //Change RSK Menu
            SwitchMenu(1, mniMenu);

            //Enable Save report menu item
            mniSaveReport.Enabled = true;

            //Disable start menu item
            mniStart.Enabled = false;

        }

        private void StartStopwatch()
        {

            timer1.Enabled = true;

            sw.Start();

            mniStart.Text = "Stop";

            //Change RSK Menu
            SwitchMenu(1, mniLap);

            //Disable Save report menu item
            mniSaveReport.Enabled = false;
        }

        private void SwitchMenu(int mnIndex, MenuItem mnItem)
        {
            if(mainMenu1.MenuItems.Count > 0)
                mainMenu1.MenuItems.RemoveAt(mnIndex);
 
            mainMenu1.MenuItems.Add(mnItem);
        }


        private void mniLap_Click(object sender, EventArgs e)
        {
            AddLap();
        }

        private void mniExit_Click(object sender, EventArgs e)
        {
            FreeResources();
            Application.Exit();
        }

        private void mniReset_Click(object sender, EventArgs e)
        {
            DialogResult msgBoxRet = MessageBox.Show("Are you sure you would like to reset this run?", "Stopwatch", MessageBoxButtons.YesNo,
                MessageBoxIcon.Question, MessageBoxDefaultButton.Button1);

            if (msgBoxRet == DialogResult.Yes)
            {
                ResetStopWatch();
            }
        }

        private void mniSaveReport_Click(object sender, EventArgs e)
        {
            //Get number of max number of reports that can be saved from registry
            if (iNumReportSaved >= iMaxNumberOfRepSaved)
            {

                string sMessage = String.Format("You have exceeded the amount of {0} summaries, the oldest summary shall be deleted. Would you like to delete the oldest summary?", iMaxNumberOfRepSaved);

                DialogResult msgBoxRet = MessageBox.Show(sMessage, "Stopwatch", 
                    MessageBoxButtons.YesNo, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1);

                if (msgBoxRet == DialogResult.Yes)
                { 
                    //Delete oldest summary
                    runRep.DeleteOldestXmlFile();

                    //Save new report and update number of report saved
                    SaveReport();
                    UpdateNumReportSaved();
                }
            }
            else
            {
                SaveReport();
                UpdateNumReportSaved();
            }
        }

        private void mniViewReport_Click(object sender, EventArgs e)
        {
            Cursor.Current = Cursors.WaitCursor;
            ViewReport viewReport = new ViewReport();
            viewReport.Show();
            Cursor.Current = Cursors.Default;
        }

        private void SaveReport()
        {

            try
            {

                Cursor.Current = Cursors.WaitCursor;

                //Prepare summary table
                string sFastest = String.Format("{0:00}:{1:00}:{2:00}.{3:000}", fastestLap.Hours, 
                    fastestLap.Minutes, fastestLap.Seconds, fastestLap.Milliseconds);

                string sSlowest = String.Format("{0:00}:{1:00}:{2:00}.{3:000}", slowestLap.Hours,
                    slowestLap.Minutes, slowestLap.Seconds, slowestLap.Milliseconds);

                string sAverage = String.Format("{0:00}:{1:00}:{2:00}.{3:000}", averageLap.Hours,
                    averageLap.Minutes, averageLap.Seconds, averageLap.Milliseconds);

                //Get Next report number
                int iReportNumber = runRep.GetNextReportNumber();

                PrepareTableSumary(sFastest, sSlowest, sAverage, TableLaps.Rows.Count, iReportNumber);

                //First, Remove tables added previously
                if (dsReport.Tables.Contains("LapsTable"))
                    dsReport.Tables.Remove(TableLaps);

                if (dsReport.Tables.Contains("SummaryTable"))
                    dsReport.Tables.Remove(TableSummary);

                //Add laps and summary tables
                dsReport.Tables.Add(TableLaps);
                dsReport.Tables.Add(TableSummary);

                //Save report
                runRep.SaveReport(dsReport);

                //Reset stopwatch after report has been saved
                ResetStopWatch();

                //Displary message
                StringBuilder sbMessage = new StringBuilder();
                sbMessage.Append("A summary report has been saved\r\n");
                sbMessage.Append(String.Format("Report: {0}\r\n", iReportNumber.ToString()));
                sbMessage.Append(String.Format("Date & Time: {0}\r\n", DateTime.Now.ToShortDateString() + " " + DateTime.Now.ToShortTimeString()));
                sbMessage.Append(String.Format("Average: {0}\r\n", sAverage));

                //Default Cursor
                Cursor.Current = Cursors.Default;

                MessageBox.Show(sbMessage.ToString(), "Stopwatch", MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1);
            }
            catch (Exception ex)
            {
                Debug.WriteLine(String.Format("Stopwatch SaverReport Exception Msg: {0} Stack Info: {1}",
                    ex.Message, ex.StackTrace));
            }
        }



        private void FreeResources()
        {
            sw.Reset();
            TableLaps.Dispose();
            TableSummary.Dispose();
            dsReport.Dispose();
        }

        private void ResetStopWatch()
        {
            sw.Reset();
            this.lbHour.Text = "00:";
            this.lbMinute.Text = "00:";
            this.lbSecond.Text = "00.";
            this.lbMilisecond.Text = "000";
            this.lbFastestLap.Text = string.Empty;
            this.lbSlowestLap.Text = string.Empty;
            this.lbAverageLap.Text = string.Empty;
            dAverage = 0;
            dTotalLaps = 0;
            this.TableLaps.Clear();
            this.TableSummary.Clear();
            this.dsReport.Clear();
            this.dataGrid1.Visible = false;
            this.mniSaveReport.Enabled = false;
            this.mniStart.Enabled = true;
        }


        private void AddLap()
        {
            try
            {
                int inumItems = TableLaps.Rows.Count;

                if (inumItems == 0)
                {
                    LapTime = SplitTime;
                    fastestLap = LapTime;
                    slowestLap = LapTime;
                    sFastestLap = String.Format("Lap {0}", inumItems + 1);
                    sSlowestLap = String.Format("Lap {0}", inumItems + 1);
                }
                else
                {
                    LapTime = SplitTime.Subtract(LastSplitTime);

                    int iCompFastest = LapTime.CompareTo(fastestLap);
                    int iCompSlowest = LapTime.CompareTo(slowestLap);

                    if (iCompFastest < 0)
                    {
                        fastestLap = LapTime;
                        sFastestLap = String.Format("Lap {0}", inumItems + 1);
                    }

                    if (iCompSlowest > 0)
                    {
                        slowestLap = LapTime;
                        sSlowestLap = String.Format("Lap {0}", inumItems + 1);
                    }
                }

                inumItems++;

                dTotalLaps += LapTime.TotalMilliseconds;

                if (inumItems > 0)
                {   
                    dAverage = dTotalLaps / inumItems;
                }

                averageLap = TimeSpan.FromMilliseconds(dAverage);


                LastSplitTime = SplitTime;

                string Lap = String.Format("(Lap{0}) {1:00}:{2:00}:{3:00}.{4:000}",
                    inumItems, LapTime.Hours, LapTime.Minutes, LapTime.Seconds, LapTime.Milliseconds);

                //Update data grid with new lap
                UpdateDataGrid(inumItems, Lap);

            }
            catch (Exception ex)
            {
                Debug.WriteLine(String.Format("Stopwatch AddLap Exception Msg: {0} Stack Info: {1}",
                    ex.Message, ex.StackTrace));
            }
        }

        private void UpdateLabels()
        {
            lbFastestLap.Text = String.Format("Fastest: \r\n{0} {1:00}:{2:00}:{3:00}.{4:000}", sFastestLap,
                fastestLap.Hours, fastestLap.Minutes, fastestLap.Seconds, fastestLap.Milliseconds);

            lbSlowestLap.Text = String.Format("Slowest: \r\n{0} {1:00}:{2:00}:{3:00}.{4:000}", sSlowestLap,
                slowestLap.Hours, slowestLap.Minutes, slowestLap.Seconds, slowestLap.Milliseconds);

            lbAverageLap.Text = String.Format("Average: \r\n{0:00}:{1:00}:{2:00}.{3:000}", averageLap.Hours,
                averageLap.Minutes, averageLap.Seconds, averageLap.Milliseconds);
        }

        private void UpdateDataGrid(int inumItems, string Lap)
        {
            try
            {
                DataRow TableRow = TableLaps.NewRow();

                TableRow["LapInfo"] = Lap;
                TableRow["LapIndex"] = inumItems;

                TableLaps.Rows.Add(TableRow);

                TableLaps.DefaultView.Sort = "LapIndex DESC";

                dataGrid1.DataSource = TableLaps;

                dataGrid1.Visible = true;

                dataGrid1.Select(0);

            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if ((e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return) 
                && this.mniStart.Text == "Start"
                && this.mniStart.Enabled)
            {
                StartStopwatch();
            }

        }
    }
}